<?php

namespace App\Http\Service;

// redis服务层 用于写redisController的逻辑
use App\Utils\MyRabbitMQ;
use App\Utils\MyRedis;
use GatewayWorker\Lib\Gateway;
use Illuminate\Support\Facades\Log;

class RedisService
{
    /**
     * 写入演示消息方法
     * @param string $uuid 锁uuid
     * @param string $uid 用户编号
     * @param string $lock_name 锁名称
     * @return void
     */
    public function insertDemoMQ(string $uuid, string $uid, string $lock_name)
    {
        // 数据
        $data = [
            'uuid' => $uuid,
            'uid' => $uid,
            'lock_name' => $lock_name,
        ];
        $exc_name = 'pcs.topic';
        $roution_key = 'test1';

        $myRabbitMQ = MyRabbitMQ::getInstance();
        // 发送消息 这个会写入到日志中记录成功失败
        $myRabbitMQ->sendMessageToTopic($data, $exc_name, $roution_key);
    }

    public function consumerDemoMQ($msg)
    {
        //消费队列 这个方法必须多次调用都是一个结果 先去验证锁 再去删除 如果验证锁已经没了就不执行 直接消费掉
        $str = $msg->body;
        $data = json_decode($str, true);
        //获取到数据 然后验证一下锁是否存在
        $myRedis = MyRedis::getInstance();
        $uuid = $myRedis->getdistributedLock($data['lock_name']);
        // 这里如果锁没了或者锁uid已经变了 说明是重复执行的消息 或者redis出问题了
        if (!$uuid || $data['uuid'] !== $uuid) {
            // 记录一下日志
            Log::channel('daily')->info('[rabbitmq][warning]消息获取redis锁失败' . $msg->body . PHP_EOL);
            return;
        }
        //执行到这里说明锁还在 并且锁uuid也是对的
        //生成一个延迟队列的消息进行演示
        $myRabiitMQ = MyRabbitMQ::getInstance();
        //获取用户连接上的websocket的client_id
        $client_id = $this->getWebsocketClentIdByUid($data['uid']);
        //10秒后删除
        $myRabiitMQ->sendDelayMessageToTopic($data, 'demo.delay.topic', 'delay1', 10);
        //调用删除的方法 这样就消费完了
        $myRedis->delDistributedLock($data['lock_name'], $data['uuid']);
        if ($client_id) {
            $this->sendWebsocketMessage($client_id, '分布式锁已删除，普通消息队列处理完成','rabbitmq-demo');
        }
        if ($client_id) {
            $this->sendWebsocketMessage($client_id, '发送延迟消息(10秒)','rabbitmq-demo');
        }
    }

    public function consumerDemoDelayMQ($msg)
    {
        // 消费延迟队列
        $str = $msg->body;
        $data = json_decode($str, true);
        //发送一个websocket消息就可以了
        //获取用户连接上的websocket的client_id
        $client_id = $this->getWebsocketClentIdByUid($data['uid']);
        if ($client_id) {
            $this->sendWebsocketMessage($client_id, '已消费延迟消息','rabbitmq-demo');
        }
    }

    /**
     * 根据重置的分布式锁名称 像客户端发送消息
     * @param array $redis_keys
     * @return array
     */
    public function distributedLockWatchDogSendMsg(array $redis_keys): array
    {
        $prev_str = 'distributed:lock:demo';
        $uids = [];
        // 判断key是否以这个开头 并且 长度是否一样
        foreach ($redis_keys as $key) {
            $key_exp = explode(':', $key);
            if (
                substr($key, 0, strlen($prev_str)) === $prev_str &&
                count($key_exp) === 4
            ) {
                $uid = $key_exp[3];
                // 获取客户端id
                $client_id = $this->getWebsocketClentIdByUid($uid);
                $uids[] = $client_id;
                if ($client_id) {
                    // 向客户端发送消息
                    $this->sendWebsocketMessage($client_id, 'watchdog重置了分布式锁');
                }
            }
        }
        return $uids;
    }

    /**
     * 发送消息到客户端
     * @param string $client_id 客户端id
     * @param string $msg 消息
     * @return void
     */
    public function sendWebsocketMessage(string $client_id, string $msg, string $type = 'redis-demo')
    {
        Gateway::$registerAddress = '127.0.0.1:9501';
        $message = json_encode(['type' => $type, 'message' => $msg, 'data' => ['message' => $msg, 'date' => date('Y-m-d H:i:s', time())]]);
        Gateway::sendToClient($client_id, $message);
    }

    /**
     * 根据userid获取连接的websocket服务器id
     * @param string $uid
     * @return mixed|false
     */
    public function getWebsocketClentIdByUid(string $uid)
    {
        Gateway::$registerAddress = '127.0.0.1:9501';
        $client_list = Gateway::getClientIdByUid($uid);
        if (!empty($client_list)) {
            return $client_list[0];
        } else {
            return false;
        }
    }
}
